package com.rivues.module.report.web.handler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.rivues.core.RivuDataContext;
import com.rivues.module.platform.web.auth.AuthInterface;
import com.rivues.module.platform.web.handler.Handler;
import com.rivues.module.platform.web.handler.ResponseData;
import com.rivues.module.platform.web.interceptor.LogIntercreptorHandler;
import com.rivues.module.platform.web.model.Auth;
import com.rivues.module.platform.web.model.DataDic;
import com.rivues.module.platform.web.model.SearchResultTemplet;
import com.rivues.module.platform.web.model.User;
import com.rivues.module.report.web.model.PublishedReport;
import com.rivues.util.RivuTools;
import com.rivues.util.iface.report.ReportFactory;
  
@Controller  
@RequestMapping("/{orgi}/user")  
public class IndexController extends Handler{  
	private final Logger logger = LoggerFactory.getLogger(LogIntercreptorHandler.class); 
	@Autowired
	private AuthInterface authInterface;
	/**
	 * 报表首页，从 接口中获取报表目录和报表，不直接读取数据库  ， tabid 表示当前操作的位置 ， 例如 ：公共文件夹，个人文件夹
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/index" , name="index" , type="user" , subtype="public")
    public ModelAndView index(HttpServletRequest request , @PathVariable String orgi ,@Valid String name , @Valid String type) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/user/index") ; 
    	String referer = null ;
    	/**
    	 * 检查SESSION里是否是有数据，如果有数据，则先清除掉数据 ， referer 在LOGIN检查失败的时候放入 在 UserController.java代码里
    	 */
    	if(request.getSession().getAttribute(RivuDataContext.REFER_STR)!=null){
    		referer = (String) request.getSession().getAttribute(RivuDataContext.REFER_STR);
    		request.getSession().removeAttribute(RivuDataContext.REFER_STR) ;
    	}
    	
    	ModelAndView modelView = request(responseData , orgi) ;
    	modelView.addObject("locationdics",this.getAllParents("0", new ArrayList<DataDic>(), orgi));
    	return referer!=null ? request(new ResponseData("redirect:"+referer), orgi) : createIndexModelAndView(modelView , orgi , "0" , super.getUser(request) , RivuDataContext.TabType.PUB.toString() , name , type , null , null) ;
    }
    
    /**
	 * 报表发布，包括生成发布链接、生产二维码、发送邮件等功能
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/publish/{reportid}" , name="publish" , type="user" , subtype="public")
    public ModelAndView publish(HttpServletRequest request , @PathVariable String orgi ,@PathVariable String reportid) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/report/publish") ; 
    	ModelAndView modelView = request(responseData , orgi) ;
    	modelView.addObject("reportid", reportid) ;
    	modelView.addObject("url", RivuTools.createMobileAccessURL(request, orgi, reportid)) ;
    	PublishedReport report = ReportFactory.getReportInstance().getReportByID(super.getUser(request), orgi, reportid) ;
    	modelView.addObject("report", report) ;
    	
    	return modelView;
    }
    
    /**
	 * 报表发布，包括生成发布链接、生产二维码、发送邮件等功能
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/sendmail/{reportid}" , name="publish" , type="user" , subtype="public")
    public ModelAndView sendmail(HttpServletRequest request , @PathVariable String orgi ,@PathVariable String reportid) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/report/sendmail") ; 
    	ModelAndView modelView = request(responseData , orgi) ;
    	modelView.addObject("reportid", reportid) ;
    	modelView.addObject("url", RivuTools.createMobileAccessURL(request, orgi, reportid)) ;
    	HashMap values = new HashMap<String , Object>();
    	String mail =  request.getParameter("mail") ;
		if(mail!=null && mail.length()>0){
			values.put("user", super.getUser(request)) ;
			values.put("url", RivuTools.createMobileAccessURL(request, orgi, reportid)) ;
			PublishedReport report = ReportFactory.getReportInstance().getReportByID(super.getUser(request), orgi, reportid) ;
			values.put("report", report) ;
			try {
				/**
				 * 从后台系统获取模板
				 */
				SearchResultTemplet templet = RivuTools.getSearchResultTemplet("R3_REPORT_PUBLISH") ;
				if(templet!=null){
					String mailcontent = RivuTools.getTemplet(templet, values) ;
					String msg = request.getParameter("text") ;
					if(msg==null){
						msg = "";
					}
					String title = request.getParameter("title") ;
					if(title == null){
						title = "" ;
					}
					RivuTools.sendMail(mail, null, request.getParameter("title") ,msg+"<br/>"+mailcontent , new ArrayList());
					modelView.addObject("msg", "邮件发送成功") ;
				}else{
					modelView.addObject("msg", "未配置邮件发送模板，请联系系统管理员。") ;
				}
			} catch (Exception e1) {
				e1.printStackTrace();
				modelView.addObject("msg", "邮件发送错误，错误消息为："+ExceptionUtils.getMessage(e1)) ;
			}
		}
    	
    	return modelView;
    }
    /**
	 * 报表发布，包括生成发布链接、生产二维码、发送邮件等功能
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/qrcode/{reportid}" , name="qrcode" , type="user" , subtype="public")
    public void qrcode(HttpServletRequest request , HttpServletResponse response ,  @PathVariable String orgi ,@PathVariable String reportid) throws Exception{  
    	response.setContentType("image/png");
        response.setHeader("Cache-Control", "no-cache, no-store");
        response.setHeader("Pragma", "no-cache");
        long time = System.currentTimeMillis();
        response.setDateHeader("Last-Modified", time);
        response.setDateHeader("Date", time);
        response.setDateHeader("Expires", time);
        
        StringBuilder sb = new StringBuilder();
        sb.append(RivuTools.createMobileAccessURL(request, orgi, reportid));
        String str = sb.toString();
        str = new String(str.getBytes("GBK"), "ISO-8859-1");
        
        Hashtable hints = new Hashtable();
        BitMatrix bitMatrix = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, 256, 256, hints);
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", response.getOutputStream());
    }
    
    
   
    
    @RequestMapping(value="/{tabid}/tabindex" , name="tabindex" , type="user" , subtype="public")
    public ModelAndView tabindex(HttpServletRequest request , @PathVariable String orgi , @PathVariable String tabid,@Valid String name , @Valid String type) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/user/index") ; 
    	ModelAndView modelView = request(responseData , orgi) ;
    	
		
    	return createIndexModelAndView(modelView , orgi , "0" , super.getUser(request) , tabid , name , type , null , null) ;
    }
    
    /**
	 * 报表首页，从 接口中获取报表目录和报表，不直接读取数据库
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/{tabid}/reportdic/create" , name="myreport_create" , type="user" , subtype="myreport_create")
    public ModelAndView myreport_create(HttpServletRequest request , @PathVariable String orgi, @PathVariable String tabid,@Valid String name , @Valid String type) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/user/index") ; 
    	ModelAndView modelView = request(responseData , orgi) ;
    	return createIndexModelAndView(modelView , orgi , RivuDataContext.SearchType.CREATE.toString() , super.getUser(request) , tabid , name , type , null , RivuDataContext.SearchType.CREATE.toString()) ;
    }
    
    /**
   	 * 报表首页，从 接口中获取报表目录和报表，不直接读取数据库
   	 * @param request
   	 * @param orgi
   	 * @return
   	 * @throws Exception
   	 */
     @RequestMapping(value="/{tabid}/reportdic/save" , name="myreport_save" , type="user" , subtype="myreport_save")
       public ModelAndView myreport_save(HttpServletRequest request , @PathVariable String orgi,  @PathVariable String tabid,@Valid String name) throws Exception{  
       	ResponseData responseData = new ResponseData("/pages/user/index") ; 
       	ModelAndView modelView = request(responseData , orgi) ;
       	return createIndexModelAndView(modelView , orgi , RivuDataContext.SearchType.SAVE.toString() , super.getUser(request) , tabid , name , null , RivuDataContext.ReportTypeEnum.SAVEAS.toString() , RivuDataContext.SearchType.SAVE.toString() ) ;
     }
       
    
    /**
	 * 报表首页，从 接口中获取报表目录和报表，不直接读取数据库
	 * @param request
	 * @param orgi
	 * @return
	 * @throws Exception
	 */
    @RequestMapping(value="/{tabid}/reportdic/{reportdic}" , name="reportdic" , type="user" , subtype="public")
    public ModelAndView reportdic(HttpServletRequest request , @PathVariable String orgi, @PathVariable String reportdic , @PathVariable String tabid,@Valid String name , @Valid String type) throws Exception{  
    	ResponseData responseData = new ResponseData("/pages/user/index") ; 
    	ModelAndView modelView = request(responseData , orgi) ;
    	modelView.addObject("locationdics",this.getAllParents(reportdic, new ArrayList<DataDic>(), orgi));
    	return createIndexModelAndView(modelView , orgi , reportdic , super.getUser(request) , tabid , name , type , null , null) ;
    }
    /**
     * 获取首页需要加载的 数据内容
     * @param modelView
     * @param orgi
     * @param reportdic		目录ID，默认传入的为 0
     * @return
     */
    private ModelAndView createIndexModelAndView(ModelAndView modelView  , String orgi , String reportdic , User user  , String tabid , String name , String type ,String reportype , String pritype){
    	/**
    	 * 目录列表，需要按照权限过滤目录，目录授权规则
    	 * 1、我创建的目录
    	 * 2、我是目录的管理员
    	 * 3、我是当前目录上级的管理员
    	 * 4、授权给我可以访问的报表
    	 */
    	List<DataDic> dicList = ReportFactory.getReportInstance().getAllReportDicList(orgi , user , tabid);
    	/**
    	 * 过滤目录权限
    	 */
    	dicAuthFilter(dicList, user , orgi);
    	
    	
    	modelView.addObject("dicList", dicList) ;
    	
    	//获取根目录权限
    	DataDic dic = new DataDic();
    	if ("0".equals(reportdic)){
    		dic.setId("0");
    		dic.setOrgi(orgi);
    		dic.setTabtype(tabid) ;
    	}else if("1".equals(reportdic)){
    		dic.setId("1");
    		dic.setOrgi(orgi);
    		dic.setTabtype(tabid) ;
    		dic.setSpsearch(false);
		}else if(RivuDataContext.SearchType.CREATE.toString().equals(reportdic)){
    		dic.setId("create");
    		dic.setOrgi(orgi);
    		dic.setTabtype(tabid) ;
    		dic.setSpsearch(false);
		}else if(RivuDataContext.SearchType.SAVE.toString().equals(reportdic)){
			dic.setId("save");
    		dic.setOrgi(orgi);
    		dic.setTabtype(tabid) ;
    		dic.setSpsearch(false);
		}else {
    		for(DataDic datadic : dicList){
    			if(datadic.getId().equals(reportdic)){
    				dic = datadic ;
    				break;
    			}
    		}
    	}
    	/**
    	 * 当前目录信息
    	 */
    	modelView.addObject("dic", dic);
    	
    	modelView.addObject("currentDicAuth", getCurrentDicAuth(dic, user, orgi , dicList));
    	
    	/**
    	 * 报表列表，需要根据用户授权信息获取报表列表，权限规则：
    	 * 1、我创建的报表
    	 * 2、我是管理员的报表
    	 * 3、授权给我可以访问的报表
    	 */
    	List<PublishedReport> reportList =  ReportFactory.getReportInstance().searchReportList(reportdic , type , name , orgi , user , tabid, reportype , pritype);
    	/**
    	 * 报表授权查询
    	 */
    	reportAuthFilter(reportList, user , orgi , dicList  , dic);
    	
    	
    	modelView.addObject("reportList",reportList) ;
		modelView.addObject("parentid", reportdic) ;
		modelView.addObject("tabid", tabid) ;
		return modelView ;
    }
    
    /**
     * 获取当前目录的访问权限
     * @param dicid
     * @param user
     * @param orgi
     * @param modelView
     */
    private DataDic getCurrentDicAuth(DataDic dic ,User user,String orgi , List<DataDic> dicList){
    	dic.setAuthList(authInterface.filterAuth(dic, user , dicList, orgi , RivuDataContext.AuthTypeEnum.DIC.toString()));
    	return dic;
    }
    
    /**
     * 目录权限过滤
     * @param list
     * @param user
     */
    private void dicAuthFilter(List<DataDic> list ,User user , String orgi){
    	
    	/**
    	 * 授权分为2种
    	 * 1、在单个报表中队对角色进行授权，在鉴权的时候直接获取用户的所有角色+reportid/dicid 查询rivu_auth进行鉴权
    	 * 2、如果该用户有修改权限，页面显示+号，增加用户组织机构。选中组织机构或者用户时，在role表中增加一类隐式角色，该角色FK关联机构码或者userid
    	 * 	  在鉴权的时候根据用户信息去查角色表，或者根据用户的组织机构去查角色，然后再根据角色进行授权
    	 */
    	/**
    	 * 1、根据用户获取到用户所属的组织机构rivu_userorgan
    	 * 2、根据组织机构查询所属的角色
    	 * 3、根据角色
    	 */
    	List<Auth> authList = authInterface.filterDicAuth(user , orgi , RivuDataContext.AuthTypeEnum.DIC.toString()); 
    	for(DataDic dic : list){
    		getDicAuth(dic , list , authList) ;
    	}
    }
    
    /**
     * 权限过滤
     * @param reportList
     */
    private List<Auth> reportAuthFilter(List<PublishedReport> reportList,User user , String orgi , List<DataDic> dicList , DataDic dic){
    	/**
    	 * 授权分为2种
    	 * 1、在单个报表中队对角色进行授权，在鉴权的时候直接获取用户的所有角色+reportid/dicid 查询rivu_auth进行鉴权
    	 * 2、如果该用户有修改权限，页面显示+号，增加用户组织机构。选中组织机构或者用户时，在role表中增加一类隐式角色，该角色FK关联机构码或者userid
    	 * 	  在鉴权的时候根据用户信息去查角色表，或者根据用户的组织机构去查角色，然后再根据角色进行授权
    	 */
    	/**
    	 * 1、根据用户获取到用户所属的组织机构rivu_userorgan
    	 * 2、根据组织机构查询所属的角色
    	 * 3、根据角色
    	 */
    	List<Auth> authList = authInterface.filterAuth(dic, user , dicList , orgi , RivuDataContext.AuthTypeEnum.REPORT.toString()); 
    	for(PublishedReport report : reportList){
    		/**
			 * 如果 为 True ， 即 覆盖父级权限 ， 否则 不覆盖
			 */
    		if(report.getUseacl()!=null && report.getUseacl().equals("true")){
    			for(Auth auth : authList){
	    			if(auth.getResourceid()!=null && auth.getResourceid().equals(report.getId())){
	    				report.getAuthList().add(auth) ;
	    			}
    			}
    		}
			if(report.getAuthList().size() ==0){
	    		for(Auth auth : authList){
	    			/**
    				 * 如果不覆盖父级权限，则：
    				 * 		1、查找报表所在的当前目录权限
    				 * 		2、从当前目录开始，一直找到 根目录为止的所有权限
    				 */
    				if(auth.getResourcetype()!=null && auth.getResourcetype().equals(Auth.RESOURCE_TYPE_DIC)){
	    				report.getAuthList().add(auth) ;
	    			}
	    		}
			}
    	}
    	return authList ;
    }
    
    /**
     * 
     * @param dic
     * @param dicList
     * @param authList
     */
    private void getDicAuth(DataDic dic  , List<DataDic> dicList , List<Auth> authList){
    	/**
		 * 第二步 ， 获取用户从当前到 根节点所有的 目录ID
		 */
		List<String> dicIDS = new ArrayList<String>();
		dicIDS.add(dic.getId()) ;
		DataDic curdic = dic ; 
		while(curdic.getParentid()!=null&&!"0".equals(curdic.getParentid()) && !"0".equals(curdic.getId()) && dicList!=null){
			for(DataDic tempDic : dicList){
				if(tempDic.getId().equals(curdic.getParentid())){
					curdic = tempDic ;
					break ;
				}
			}
			dicIDS.add(curdic.getId()) ;
			if(curdic.getType()!=null && "true".equals(curdic.getType())){
				break ;
			}
		}
		if(curdic.getParentid()!=null && !(curdic.getType()!=null && "true".equals(curdic.getType()))){
			dicIDS.add(curdic.getParentid()) ;
		}
		for(Auth auth : authList){
			boolean found = false ;
			for(String dicid : dicIDS){
				if(dicid.equals(auth.getResourceid())){
					found = true ;
				}
			}
			if(found){
				dic.getAuthList().add(auth) ;
			}
		}
    }
    @SuppressWarnings("unchecked")
	private List<DataDic> getAllParents(String id,List<DataDic> dics,String orgi){
		if("0".equals(id))return dics;
		
		List<DataDic> diclist = super.getService().findAllByCriteria(DetachedCriteria.forClass(DataDic.class).add(Restrictions.eq("orgi",orgi)));
		DataDic parent = null ;
		while(parent==null){
			for(DataDic type : diclist){
				if(type.getId().equals(id)){
					parent = type ;
					id = parent.getParentid();
					break ;
				}
			}
			if(parent!=null){
				dics.add(parent);
				parent = null;
			}else{
				break ;
			}
		}
		return dics;
	}
} 